﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;



namespace LCH
{
    public partial class LchMeshPainter
    {
        /// <summary>
        /// 网格简化参数配置类，用于控制网格简化过程中的各种参数和约束条件
        /// </summary>
        [System.Serializable]
        private class SimplifyParameters
        {

            // 要折叠的边的数量。设置为 -1 表示不限制折叠边的数量，由其他条件决定
            public int edgesToCollapse = -1;

            // 目标面的数量。设置为 -1 表示不根据面的数量来限制简化，由其他条件决定
            public int targetFaceCount = -1;

            // 最大误差。当简化过程中产生的误差超过该值时，停止简化操作。设置为 -1 表示不限制误差
            public float maximumError = -1;

            // 是否重新计算顶点位置。若为 true，则在简化过程中会重新计算顶点的位置以优化简化效果
            public bool recalculateVertexPositions = true;

            // 是否防止产生非流形边。非流形边是指不符合流形拓扑结构的边，若为 true，则在简化时会避免产生此类边
            public bool preventNonManifoldEdges = false;

            // 边界权重。用于控制模型边界部分在简化过程中的保留程度，值越大，边界越不容易被简化
            public float borderWeight = 1.0f;

            // 材质接缝权重。用于控制材质接缝处的保留程度，值越大，材质接缝处越不容易被简化
            public float materialSeamWeight = 1.0f;

            // UV 接缝权重。用于控制 UV 映射接缝处的保留程度，值越大，UV 接缝处越不容易被简化
            public float uvSeamWeight = 1.0f;

            // UV2 接缝权重。用于控制第二套 UV 映射接缝处的保留程度，值越大，UV2 接缝处越不容易被简化
            public float uv2SeamWeight = 0.0f;

            // 折痕权重。用于控制模型折痕处的保留程度，值越大，折痕处越不容易被简化
            public float creaseWeight = 0.0f;

            // 骨骼权重保护。用于保护受骨骼影响的顶点，值越大，受骨骼影响的顶点越不容易被简化
            public float boneWeightProtection = 0.1f;

            // 顶点颜色保护。用于保护具有顶点颜色的顶点，值越大，具有顶点颜色的顶点越不容易被简化
            public float vertexColorProtection = 0.0f;

            // 每个顶点的最大边数。限制每个顶点连接的边的最大数量，以控制简化过程中的拓扑结构
            public int maxEdgesPerVertex = 18;

            // 是否检查拓扑结构。若为 true，则在简化过程中会检查模型的拓扑结构，确保简化后的模型拓扑合理
            public bool checkTopology = true;

        }

        /// <summary>
        /// 网格简化核心类，实现基于边折叠（Edge Collapse）的网格简化算法
        /// 使用二次误差度量（Quadric Error Metrics）来评估和优化简化过程
        /// </summary>
        private class Simplify
        {
            SimplifyParameters _parameters;

            /// <summary>
            /// 进度回调委托，用于报告简化过程的进度
            /// </summary>
            public ProgressDelegate progressDelegate = null;

            /// <summary>
            /// 是否本地化误差计算，用于优化误差度量的精度
            /// </summary>
            const bool localizeErrors = true;

            /// <summary>
            /// 非流形边惩罚值，用于防止产生非流形拓扑结构
            /// </summary>
            const float kMeshPenaltyNonManifold = 1e7f;
            /// <summary>
            /// 不良拓扑结构惩罚值，用于防止产生拓扑翻转等问题
            /// </summary>
            const float kMeshPenaltyBadTopology = 1e6f;
            /// <summary>
            /// 顶点最大边数惩罚值，用于防止产生过度连接的顶点
            /// </summary>
            const float kMeshPenaltyMaxEdgesPerVertex = 1e5f;

            /// <summary>
            /// 最小堆，用于按成本排序边折叠操作
            /// </summary>
            MinHeap<CollapseInfo> heap = null;
            /// <summary>
            /// 堆节点数组，存储每条边对应的堆节点引用
            /// </summary>
            HeapNode<CollapseInfo>[] heapNodes = null;
            /// <summary>
            /// 每个顶点的平面距离误差，用于计算边折叠的成本
            /// </summary>
            PlaneDistanceError[] pdePerVertex = null;
            /// <summary>
            /// 当前处理的网格对象
            /// </summary>
            MeshEdges mesh = null;

            /// <summary>
            /// 进度报告分组数量，用于控制进度更新的频率
            /// </summary>
            const int kProgressGroups = 100;
            /// <summary>
            /// 进度报告时间间隔（秒）
            /// </summary>
            const float kProgressInterval = 0.2f;

            /// <summary>
            /// 网格尺寸，用于归一化误差计算
            /// </summary>
            float meshSize = 1.0f;
            /// <summary>
            /// 是否跳过惩罚计算，用于优化性能
            /// </summary>
            bool noPenalties = false;

            /// <summary>
            /// 将网格三角化，将四边形面转换为三角形面
            /// 对于四边形面，选择较短的对角线进行分割
            /// </summary>
            private static void Triangulate(BaseMesh mesh)
            {
                if (mesh.topology == MeshTopology.Triangles) return;

                int faceIndex, cornerIndex;

                int numFaces = mesh.faceCount();
                Vector3[] coords = new Vector3[4];
                for (faceIndex = 0; faceIndex < numFaces; ++faceIndex)
                {
                    Face f = mesh.faces[faceIndex];
                    if (f.valid && f.cornerCount == 4)
                    {
                        Face fnew = new Face(3);

                        fnew.material = f.material;
                        for (cornerIndex = 0; cornerIndex < 4; ++cornerIndex)
                        {
                            coords[cornerIndex] = mesh.vertices[f.v[cornerIndex]].coords;
                        }

                // 计算两条对角线的长度，选择较短的对角线进行分割
                Vector3 diag02 = coords[0] - coords[2];
                Vector3 diag13 = coords[1] - coords[3];
                if (diag02.sqrMagnitude < diag13.sqrMagnitude)
                {
                    // 沿对角线0-2分割，创建三角形(0,2,3)
                    fnew.CopyVertexInfoFromFace(f, 0, 0);
                    fnew.CopyVertexInfoFromFace(f, 2, 1);
                    fnew.CopyVertexInfoFromFace(f, 3, 2);
                }
                else
                {
                    // 沿对角线1-3分割，创建三角形(3,1,2)，原面变为(0,1,3)
                    fnew.CopyVertexInfoFromFace(f, 3, 0);
                    fnew.CopyVertexInfoFromFace(f, 1, 1);
                    fnew.CopyVertexInfoFromFace(f, 2, 2);
                    f.CopyVertexInfoFromFace(f, 3, 2);
                }
                        mesh.AddFace(fnew);

                        f.cornerCount = 3;
                    }
                }

                // 重新初始化顶点的关联面列表
                int numVerts = mesh.vertCount();
                for (int vertIndex = 0; vertIndex < numVerts; ++vertIndex)
                {
                    mesh.vertices[vertIndex].linkedFaces = new IndexList(18);
                }
                numFaces = mesh.faceCount();

                // 重建顶点到面的关联关系
                for (faceIndex = 0; faceIndex < numFaces; ++faceIndex)
                {
                    Face f = mesh.faces[faceIndex];
                    for (cornerIndex = 0; cornerIndex < f.cornerCount; ++cornerIndex)
                    {
                        mesh.vertices[f.v[cornerIndex]].linkedFaces.Add(faceIndex);
                    }
                }
                mesh.topology = MeshTopology.Triangles;
            }
            /// <summary>
            /// 将网格三角化并重建边拓扑结构
            /// 在三角化后，为新生成的三角形面创建对应的边，并重新生成顶点-边关联关系
            /// </summary>
            private static void TriangulateWithEdges(MeshEdges mesh)
            {
                if (mesh.topology == MeshTopology.Triangles) return;

                int oldNumFaces = mesh.faceCount();
                Triangulate((BaseMesh)mesh);
                int newNumFaces = mesh.faceCount();
                for (int faceIndex = oldNumFaces; faceIndex < newNumFaces; ++faceIndex)
                {
                    Face f = mesh.faces[faceIndex];
                    Edge e = new Edge(f.v[0], f.v[1]);
                    mesh.AddEdge(e);
                }
                mesh.RegenerateVertexLinkedEdges();
                mesh.GenerateEdgeTopology();

            }

            /// <summary>
            /// 执行网格简化操作的主入口方法
            /// 根据参数选择不同的简化策略：按边数、按面数或按误差阈值
            /// </summary>
            public void Execute(ref MeshEdges mesh, SimplifyParameters parameters)
            {
                if (mesh.vertCount() == 0) return;

                if (mesh.topology != MeshTopology.Triangles)
                {
                    TriangulateWithEdges(mesh);
                }

                if (progressDelegate != null) progressDelegate("Initialize", 0.0f);
                InitializeCollapsing(mesh, parameters);
                if (parameters.edgesToCollapse > 0)
                {
                    Collapse(parameters.edgesToCollapse);
                }
                else if (parameters.targetFaceCount > 0)
                {
                    int totalFacesToRemove = mesh.numValidFaces - parameters.targetFaceCount;
                    int progressCounter = 0;
                    float t = Time.realtimeSinceStartup + kProgressInterval;
                    while (mesh.numValidFaces > parameters.targetFaceCount)
                    {
                        Collapse(1);
                        progressCounter--;
                        if (progressCounter <= 0)
                        {
                            progressCounter = kProgressGroups;
                            if (Time.realtimeSinceStartup - t > kProgressInterval && progressDelegate != null)
                            {
                                t = Time.realtimeSinceStartup;
                                int facesRemoved = totalFacesToRemove - (mesh.numValidFaces - parameters.targetFaceCount);
                                progressDelegate("Mesh Faces " + mesh.numValidFaces + "->" + parameters.targetFaceCount, 0.1f + 0.9f * ((float)facesRemoved) / ((float)totalFacesToRemove));
                            }
                        }
                    }
                }
                else if (parameters.maximumError > 0)
                {
                    meshSize = _determineMeshSize(mesh);
                    int totalFacesToRemove = mesh.numValidFaces;
                    float t = Time.realtimeSinceStartup + kProgressInterval;
                    float costThreshold = meshSize * parameters.maximumError * 0.001f;
                    costThreshold *= costThreshold;
                    int progressCounter = 0;
                    while (true)
                    {
                        int num = Collapse(1, costThreshold);
                        if (num == 0) break;
                        progressCounter--;
                        if (progressCounter <= 0)
                        {
                            progressCounter = kProgressGroups;
                            if (Time.realtimeSinceStartup - t > kProgressInterval && progressDelegate != null)
                            {
                                t = Time.realtimeSinceStartup;
                                int facesRemoved = totalFacesToRemove - (mesh.numValidFaces - parameters.targetFaceCount);
                                progressDelegate("Mesh Faces " + mesh.numValidFaces + "->" + parameters.targetFaceCount, 0.1f + 0.9f * ((float)facesRemoved) / ((float)totalFacesToRemove)); // TODO: do this better
                            }
                        }
                    }
                }
                mesh.RebuildMesh();
                Cleanup();
            }

            /// <summary>
            /// 初始化边折叠操作
            /// 计算每个顶点的平面距离误差，为每条边计算折叠成本，并构建最小堆
            /// </summary>
            private void InitializeCollapsing(MeshEdges _mesh, SimplifyParameters parameters)
            {
                mesh = _mesh;
                _parameters = parameters;
                if (_mesh.hasBoneWeights == false) parameters.boneWeightProtection = 0.0f;
                if (_mesh.hasVertexColors == false) parameters.vertexColorProtection = 0.0f;
                _calculatePdePerVertex();
                int numEdges = mesh.edgeCount();
                heapNodes = new HeapNode<CollapseInfo>[numEdges];
                heap = new MinHeap<CollapseInfo>();
                int progressCounter = kProgressGroups;
                float t = Time.realtimeSinceStartup;
                for (int i = 0; i < numEdges; ++i)
                {
                    CollapseInfo pc = new CollapseInfo();
                    _calculateEdgeCost(i, pc);
                    heapNodes[i] = heap.Insert(new HeapNode<CollapseInfo>(pc.cost, pc));
                    progressCounter--;
                    if (progressCounter <= 0)
                    {
                        progressCounter = kProgressGroups;
                        if (Time.realtimeSinceStartup - t > kProgressInterval && progressDelegate != null)
                        {
                            t = Time.realtimeSinceStartup;
                            progressDelegate("Initialize Edge " + i + "/" + numEdges, 0.1f * ((float)i) / ((float)numEdges));
                        }
                    }
                }
                noPenalties = (parameters.checkTopology == false
                    && parameters.maxEdgesPerVertex == 0
                    && parameters.preventNonManifoldEdges == false
                    && parameters.boneWeightProtection <= 0.0f
                    && parameters.vertexColorProtection <= 0.0f);
            }

            /// <summary>
            /// 清理资源，释放所有临时对象和引用
            /// </summary>
            public void Cleanup()
            {
                _parameters = null;
                progressDelegate = null;
                pdePerVertex = null;
                heapNodes = null;
                heap = null;
                mesh = null;
            }

            /// <summary>
            /// 需要更新的边列表，用于存储边折叠后需要重新计算成本的边
            /// </summary>
            List<int> _edgesToUpdate = new List<int>(32);
            /// <summary>
            /// 周围顶点列表，用于存储边折叠后受影响的顶点
            /// </summary>
            List<int> _surroundingVerts = new List<int>(32);

            /// <summary>
            /// 执行边折叠操作
            /// 从最小堆中取出成本最低的边进行折叠，并更新受影响边的成本
            /// </summary>
            /// <param name="numEdgesToCollapse">要折叠的边数量</param>
            /// <param name="maxCost">最大允许成本，超过此成本的边不会被折叠</param>
            /// <returns>实际折叠的边数量</returns>
            public int Collapse(int numEdgesToCollapse = 1, float maxCost = 1e6f)
            {
                int collapsesDone = 0;
                while (numEdgesToCollapse > 0)
                {
                    HeapNode<CollapseInfo> node;
                    do
                    {
                        node = heap.Extract();
                    } while (node != null && mesh.isVertexPairValid(node.obj.vp) == false);
                    if (node == null) break;

                    if (node.heapValue > maxCost) break;

                    CollapseInfo cinfo = node.obj;
                    VertexPair vp = cinfo.vp;
                    // 合并两个顶点的平面距离误差，用于后续计算
                    pdePerVertex[vp.v[0]].OpAdd(pdePerVertex[vp.v[1]]);

                    // 执行边折叠，将两个顶点合并为一个
                    int vindex = mesh.CollapseVertexPair(cinfo);
                    _edgesToUpdate.Clear();
                    _surroundingVerts.Clear();

                    // 收集折叠后新顶点周围的所有顶点
                    mesh.CollectVerticesAroundVertex(vindex, ref _surroundingVerts);

                    // 使用唯一标记来避免重复处理同一条边
                    int mark = mesh.GetUniqueTag();
                    for (int i = 0; i < _surroundingVerts.Count; ++i)
                    {
                        List<int> lEdges = mesh.linkedEdgesForVert(_surroundingVerts[i]);
                        for (int j = 0; j < lEdges.Count; ++j)
                        {
                            int edgeIndex = lEdges[j];
                            Edge e = mesh.edges[edgeIndex];
                            if (e.mark != mark)
                            {
                                e.mark = mark;
                                if (mesh.IsEdgeValid(edgeIndex))
                                {
                                    _edgesToUpdate.Add(edgeIndex);
                                }
                            }
                        }
                    }

                    // 更新受影响边的成本和惩罚值
                    // 如果边包含新顶点，需要重新计算成本；否则只需更新惩罚值
                    for (int i = 0; i < _edgesToUpdate.Count; ++i)
                    {
                        int edgeIndex = _edgesToUpdate[i];
                        HeapNode<CollapseInfo> hnode = heapNodes[edgeIndex];
                        // 如果边包含新顶点，需要重新计算成本（因为顶点位置改变了）
                        if (mesh.edges[edgeIndex].ContainsVertex(vindex))
                        {
                            _calculateEdgeCost(edgeIndex, hnode.obj);
                        }
                        else
                        {
                            // 否则只需更新惩罚值（因为拓扑关系可能改变了）
                            _updateEdgePenalties(edgeIndex, hnode.obj, vindex);
                        }
                        // 更新堆中该边的位置，保持堆的有序性
                        heap.Update(hnode, hnode.obj.cost);
                    }

                    numEdgesToCollapse--;
                    collapsesDone++;
                }
                return collapsesDone;
            }

            /// <summary>
            /// 临时使用的平面距离误差对象，用于避免重复创建
            /// </summary>
            PlaneDistanceError pde = new PlaneDistanceError();

            /// <summary>
            /// 重新计算指定顶点的平面距离误差
            /// 遍历该顶点关联的所有有效面，累加每个面的平面误差
            /// </summary>
            void _recalculateVertexPde(int vertexIndex)
            {
                IndexList faceIndexes = mesh.vertices[vertexIndex].linkedFaces;
                pdePerVertex[vertexIndex].Clear();
                for (int i = 0; i < faceIndexes.Count; ++i)
                {
                    Face f = mesh.faces[faceIndexes[i]];
                    if (f.valid)
                    {
                        _determinePdeForFace(faceIndexes[i], ref pde);
                        pdePerVertex[vertexIndex].OpAdd(pde);
                    }
                }
            }

            /// <summary>
            /// 计算每个顶点的平面距离误差
            /// 为每个顶点构建基于周围面的误差项，该误差项是所有面到该顶点距离平方的总和
            /// 同时处理边界、接缝等约束条件
            /// </summary>
            void _calculatePdePerVertex()
            {
                if (mesh == null)
                {
                    Debug.LogError("Mesh is not initialized. Could not calculate plane distance errors."); return;
                }

                int numVerts = mesh.vertCount();
                int numFaces = mesh.faceCount();
                // Initialize pdes
                pdePerVertex = new PlaneDistanceError[numVerts];
                for (int i = 0; i < numVerts; ++i) pdePerVertex[i] = new PlaneDistanceError();

                PlaneDistanceError pde = new PlaneDistanceError();
                // 遍历所有面，将每个面的平面误差累加到其顶点的误差中
                for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex)
                {
                    _determinePdeForFace(faceIndex, ref pde);
                    Face face = mesh.faces[faceIndex];
                    for (int i = 0; i < face.cornerCount; ++i)
                    {
                        pdePerVertex[face.v[i]].OpAdd(pde);
                    }
                }

                // 处理网格约束条件（边界、接缝等）
                int numEdges = mesh.edgeCount();
                // 查找边界顶点和网格不连续处，为这些特殊边添加额外的约束误差 
                for (int edgeIndex = 0; edgeIndex < numEdges; ++edgeIndex)
                {
                    if (_determineEdgeContraintForEdge(edgeIndex, ref pde))
                    {
                        Edge edge = mesh.edges[edgeIndex];
                        pdePerVertex[edge.v[0]].OpAdd(pde);
                        pdePerVertex[edge.v[1]].OpAdd(pde);
                    }
                }
            }

            /// <summary>
            /// 计算指定面的平面距离误差
            /// 基于面的法向量和面积构建平面方程，用于后续的误差计算
            /// </summary>
            void _determinePdeForFace(int faceIndex, ref PlaneDistanceError pde)
            {
                Face face = mesh.faces[faceIndex];
                Vector3 n = mesh.CalculateFaceNormal(faceIndex);

                // Create error struct from the plane of the face (using face normal)
                float offset = -Vector3.Dot(n, mesh.vertices[face.v[0]].coords);
                pde.Set(n.x, n.y, n.z, offset, mesh.CalculateFaceArea(faceIndex));
                // Multiply by face area (factor) for weighting
                pde.OpMul(pde.Factor());
            }

            /// <summary>
            /// 确定指定边的约束条件
            /// 检查边是否为边界、折痕、材质接缝或UV接缝，并返回相应的权重
            /// </summary>
            bool _determineEdgeContraintForEdge(int edgeIndex, ref PlaneDistanceError pde)
            {
                float weight = 0.0f;
                if (mesh.IsEdgeBorder(edgeIndex))
                {
                    weight = _parameters.borderWeight;
                }
                else
                {
                    if (mesh.edges[edgeIndex].crease == 1.0f) weight = Mathf.Max(weight, _parameters.creaseWeight);
                    if (mesh.numMaterials > 1 && mesh.isEdgeMaterialSeam(edgeIndex)) weight = Mathf.Max(weight, _parameters.materialSeamWeight);
                    if (mesh.hasUV1 && mesh.IsEdgeUV1Seam(edgeIndex)) weight = Mathf.Max(weight, _parameters.uvSeamWeight);
                    if (mesh.hasUV2 && mesh.IsEdgeUV2Seam(edgeIndex)) weight = Mathf.Max(weight, _parameters.uv2SeamWeight);
                }
                if (weight > 0.0f)
                {
                    _determinePdeToConstrainEdge(mesh.edges[edgeIndex], weight, ref pde);
                    return true;
                }
                return false;
            }

            /// <summary>
            /// 临时使用的平面距离误差对象，用于边约束计算
            /// </summary>
            PlaneDistanceError pde2 = new PlaneDistanceError();

            /// <summary>
            /// 计算用于约束边的平面距离误差
            /// 为边界、接缝等特殊边创建约束平面，防止这些边在简化过程中被过度修改
            /// </summary>
            void _determinePdeToConstrainEdge(Edge edge, float weight, ref PlaneDistanceError pde)
            {
                int vi0 = edge.v[0];
                int vi1 = edge.v[1];

                Vector3 v0 = mesh.vertices[vi0].coords;
                Vector3 vEdge = mesh.vertices[vi1].coords - v0;
                Vector3 edgeNormal;
                pde.Clear();
                float mag = vEdge.sqrMagnitude;
                for (int i = 0; i < edge.linkedFaces.Count; ++i)
                {
                    edgeNormal = mesh.faces[edge.linkedFaces[i]].normal;

                    Vector3 n = Vector3.Cross(vEdge, edgeNormal); // normal to edge and face
                    NormalizeSmallVector(ref n);

                    float d = -Vector3.Dot(n, v0);
                    pde2.Set(n.x, n.y, n.z, d, mag);
                    // Multiply by face area (factor) for weighting
                    pde2.OpMul(pde2.Factor() * weight * 0.5f);
                    pde.OpAdd(pde2);
                }
            }

            /// <summary>
            /// 存储顶点对中第一个顶点关联的面索引列表
            /// </summary>
            IndexList faces0 = new IndexList(32);
            /// <summary>
            /// 存储顶点对中第二个顶点关联的面索引列表
            /// </summary>
            IndexList faces1 = new IndexList(32);
            /// <summary>
            /// 存储两个顶点共同关联的面索引列表
            /// </summary>
            IndexList commonFaces = new IndexList(32);

            /// <summary>
            /// 计算指定边的折叠成本
            /// 基于平面距离误差计算最优折叠位置和成本，如果启用重新计算顶点位置，会寻找误差最小的新顶点位置
            /// </summary>
            void _calculateEdgeCost(int edgeIndex, CollapseInfo cinfo)
            {
                Edge edge = mesh.edges[edgeIndex];
                int vindex0 = edge.v[0];
                int vindex1 = edge.v[1];
                Vertex v0 = mesh.vertices[vindex0];
                Vertex v1 = mesh.vertices[vindex1];

                cinfo.vp = edge;

                PlaneDistanceError pde = pdePerVertex[edge.v[0]] + pdePerVertex[edge.v[1]];

                if (_parameters.recalculateVertexPositions)
                {
                    // 尝试计算最优顶点位置以最小化误差
                    if (mesh.IsEdgeBorder(edgeIndex) == false && pde.OptimalVertex(ref cinfo.targetPosition))
                    {
                        // 成功计算到最优位置
                        cinfo.cost = (float)pde.CalculateError(cinfo.targetPosition);
                    }
                    else if (pde.OptimalVertexLinear(ref cinfo.targetPosition, v0.coords, v1.coords))
                    {
                        // 误差项不可解，尝试在v0到v1的直线上寻找最优位置
                        cinfo.cost = (float)pde.CalculateError(cinfo.targetPosition);
                    }
                    else
                    {
                        // 从两个端点和中点中选择误差最小的位置
                        Vector3 tp = 0.5f * (v0.coords + v1.coords);
                        double error0 = pde.CalculateError(v0.coords);
                        double error1 = pde.CalculateError(v1.coords);
                        double error2 = pde.CalculateError(tp);
                        if (error0 < error1)
                        {
                            if (error0 < error2)
                            {
                                cinfo.targetPosition = v0.coords; cinfo.cost = (float)error0;
                            }
                            else
                            {
                                cinfo.targetPosition = tp; cinfo.cost = (float)error2;
                            }
                        }
                        else
                        {
                            if (error1 < error2)
                            {
                                cinfo.targetPosition = v1.coords; cinfo.cost = (float)error1;
                            }
                            else
                            {
                                cinfo.targetPosition = tp; cinfo.cost = (float)error2;
                            }
                        }
                    }
                }
                else
                {
                    // 不重新计算顶点位置，直接选择两个端点中误差较小的一个
                    double error0 = pde.CalculateError(v0.coords);
                    double error1 = pde.CalculateError(v1.coords);
                    if (error0 < error1)
                    {
                        cinfo.targetPosition = v0.coords;
                        cinfo.cost = (float)error0;
                    }
                    else
                    {
                        cinfo.targetPosition = v1.coords;
                        cinfo.cost = (float)error1;
                    }
                }

                // 本地化误差计算，通过除以因子来归一化误差值
                // 注意：对于边界边（欠定义）可能不太准确
                if (localizeErrors) cinfo.cost *= 1.0f / ((float)pde.Factor());

                cinfo.positionCost = cinfo.cost;

                if (noPenalties == false)
                {
                    _updateEdgePenalties(edgeIndex, cinfo, -1);
                }
            }

            /// <summary>
            /// 更新边的惩罚值
            /// 检查边折叠是否会产生非流形边、不良拓扑或过度连接的顶点，并应用相应的惩罚
            /// 同时考虑骨骼权重和顶点颜色的保护
            /// </summary>
            void _updateEdgePenalties(int edgeIndex, CollapseInfo cinfo, int movedVertexIndex = -1)
            {
                Edge edge = mesh.edges[edgeIndex];
                int vindex0 = edge.v[0];
                int vindex1 = edge.v[1];
                Vertex v0 = mesh.vertices[vindex0];
                Vertex v1 = mesh.vertices[vindex1];

                faces0.Clear();
                faces1.Clear();
                commonFaces.Clear();

                // 检查之前是否有惩罚值
                bool hadPenalty = (cinfo.cost >= kMeshPenaltyMaxEdgesPerVertex);

                // 重置成本为基础位置成本
                cinfo.cost = cinfo.positionCost;
                // 收集边折叠涉及的面：第一个顶点的面、第二个顶点的面、共同的面
                mesh.CollectCollapseFacesForVertexPair(mesh.edges[edgeIndex], faces0, faces1, commonFaces);

                // 应用各种惩罚值

                int filterTag = 0;

                if (_parameters.preventNonManifoldEdges && _producesNonManifold(mesh, cinfo))
                {
                    cinfo.cost += kMeshPenaltyNonManifold; // largest penalty first
                }
                else
                {
                    if (movedVertexIndex != -1 && hadPenalty == false)
                    {
                        // 对于非新创建的且之前没有惩罚的折叠信息，可以跳过与移动顶点无关的面
                        // 使用标记来过滤需要检查的面，提高性能
                        filterTag = mesh.GetUniqueTag();
                        IndexList linkedFaces = mesh.vertices[movedVertexIndex].linkedFaces;
                        int count = linkedFaces.Count;
                        for (int i = 0; i < count; ++i) mesh.faces[linkedFaces[i]].mark = filterTag;
                    }
                    if (_parameters.checkTopology && _producesBadTopology(mesh, cinfo, filterTag))
                    {
                        // Apply penalties for bad collapses
                        cinfo.cost += kMeshPenaltyBadTopology;
                    }
                    else if (_parameters.maxEdgesPerVertex > 0 && _vertexDegreeAfterCollapse(mesh, cinfo) > _parameters.maxEdgesPerVertex)
                    {
                        // 避免产生过度连接的顶点（星形结构），防止一个顶点连接过多边
                        cinfo.cost += kMeshPenaltyMaxEdgesPerVertex;
                    }
                }

                // 额外的惩罚值：骨骼权重和顶点颜色保护			
                float val = 0.0f;
                if (_parameters.boneWeightProtection > 0.0f)
                {
                    val += BoneWeightDeltaSqr(v0.boneWeight, v1.boneWeight) * _parameters.boneWeightProtection;
                }
                if (_parameters.vertexColorProtection > 0.0f)
                {
                    val += ColorDeltaSqr(v0.color, v1.color) * _parameters.vertexColorProtection;
                }
                if (val != 0.0f)
                {
                    cinfo.cost += 0.1f * val * mesh.EdgeLengthSqr(edgeIndex);
                }
            }

            /// <summary>
            /// 计算边折叠后顶点的度数（连接的面的数量）
            /// 用于检查折叠后是否会产生过度连接的顶点
            /// </summary>
            int _vertexDegreeAfterCollapse(BaseMesh mesh, CollapseInfo ci)
            {
                return faces0.Count + faces1.Count;
            }

            /// <summary>
            /// 检查边折叠是否会产生非流形边
            /// 通过比较两个顶点的邻接顶点数量来判断，如果折叠后邻接顶点数量不匹配，则会产生非流形结构
            /// </summary>
            bool _producesNonManifold(BaseMesh mesh, CollapseInfo ci)
            {
                List<Vertex> vertices = mesh.vertices;
                List<Face> faces = mesh.faces;
                int common = commonFaces.Count;
                int tag = mesh.GetUniqueTag();

                // 标记第一个顶点关联的所有面的顶点
                int count = faces0.Count;
                int[] faceArray = faces0.array;
                for (int i = 0; i < count; ++i)
                {
                    int[] v = faces[faceArray[i]].v;
                    vertices[v[0]].mark = tag;
                    vertices[v[1]].mark = tag;
                    vertices[v[2]].mark = tag;
                }
                // 检查第二个顶点关联的面，如果顶点已被标记，说明是共同邻接顶点
                count = faces1.Count;
                faceArray = faces1.array;
                for (int i = 0; i < count; ++i)
                {
                    int[] v = faces[faceArray[i]].v;
                    // 如果顶点被标记，说明是共同邻接顶点，减少计数
                    // 如果计数小于0，说明邻接顶点数量不匹配，会产生非流形结构
                    if (vertices[v[0]].mark == tag) { common--; if (common < 0) return true; vertices[v[0]].mark = 0; }
                    if (vertices[v[1]].mark == tag) { common--; if (common < 0) return true; vertices[v[1]].mark = 0; }
                    if (vertices[v[2]].mark == tag) { common--; if (common < 0) return true; vertices[v[2]].mark = 0; }
                }

                return common != 0;
            }

            /// <summary>
            /// 检查边折叠是否会产生不良拓扑结构（如面翻转）
            /// 通过构造约束平面并检查新顶点位置是否在平面的正确一侧来判断
            /// </summary>
            bool _producesBadTopology(BaseMesh mesh, CollapseInfo ci, int filterTag = 0)
            {
                Vector3 p1, origPos, vOpp, faceNormal, normal;
                List<Vertex> vertices = mesh.vertices;
                List<Face> faces = mesh.faces;

                for (int k = 0; k < 2; ++k)
                {
                    int vindex = ci.vp.v[k];
                    origPos = vertices[vindex].coords;
                    IndexList linkedFacesList = (k == 0) ? faces0 : faces1;
                    int[] linkedFaces = linkedFacesList.array;
                    for (int i = 0; i < linkedFacesList.Count; ++i)
                    {
                        Face f = faces[linkedFaces[i]];
                        if (f.mark >= filterTag)
                        {
                            // 构造约束平面：使用对边和面的法向量
                            // 检查新顶点位置是否在平面的正确一侧，如果不在则会产生面翻转
                            if (f.v[0] == vindex)
                            {
                                p1 = vertices[f.v[1]].coords;
                                vOpp = vertices[f.v[2]].coords - p1;
                            }
                            else if (f.v[1] == vindex)
                            {
                                p1 = vertices[f.v[2]].coords;
                                vOpp = vertices[f.v[0]].coords - p1;
                            }
                            else
                            {
                                p1 = vertices[f.v[0]].coords;
                                vOpp = vertices[f.v[1]].coords - p1;
                            }

                            // 计算面的法向量
                            faceNormal = Vector3.Cross(vOpp, origPos - p1);
                            // 计算约束平面的法向量（垂直于对边和面法向量）
                            normal = Vector3.Cross(faceNormal, vOpp);
                            // 如果新顶点位置在平面的负侧，说明会产生面翻转
                            if (Vector3.Dot(ci.targetPosition - p1, normal) < 0.0f) return true;
                        }
                    }
                }
                return false;
            }

        

            /// <summary>
            /// 计算网格的尺寸（包围盒的最大边长）
            /// 用于归一化误差计算，使误差阈值在不同尺寸的网格上具有可比性
            /// </summary>
            float _determineMeshSize(BaseMesh mesh)
            {
                Vector3 min = mesh.vertices[0].coords;
                Vector3 max = min;
                int numVerts = mesh.vertCount();
                for (int i = 0; i < numVerts; ++i)
                {
                    Vector3 c = mesh.vertices[i].coords;
                    if (c.x < min.x) min.x = c.x;
                    else if (c.x > max.x) max.x = c.x;
                    if (c.y < min.y) min.y = c.y;
                    else if (c.y > max.y) max.y = c.y;
                    if (c.z < min.z) min.z = c.z;
                    else if (c.z > max.z) max.z = c.z;
                }
                // 计算包围盒尺寸
                max -= min;
                float result = max.x;
                // 更新网格尺寸为最大边长
                if (max.y > meshSize) meshSize = max.y;
                if (max.z > meshSize) meshSize = max.z;
                return result;
            }
        }
    }
}
